package com.fi.common.domain.vo;


import cn.hutool.core.collection.CollUtil;
import com.fi.recogn.dto.table.Cell;
import com.fi.recogn.dto.table.Line;
import com.fi.recogn.dto.table.Table;
import com.fi.recogn.enums.ExcelOcrTypeEnum;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;

import java.util.*;
import java.util.stream.Collectors;

import static com.fi.recogn.enums.ExcelOcrTypeEnum.PLAIN;
import static com.fi.recogn.enums.ExcelOcrTypeEnum.TABLE_WITH_LINE;
import static com.fi.recogn.util.FileConvertUtil.docConvertDocx;

/**
 * @Description TODO word 元数据信息
 * @Author lujiahong
 * @Date 2024/8/12 17:50
 */
@Slf4j
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true, exclude = {"xwpfDocument", "hwpfDocument"})
public class FileDataWord extends FileData{

    private Boolean isDoc2Html;//是否doc转为html
    private Boolean isDocx2Html;//是否docx转为html
    private XWPFDocument xwpfDocument;//poi word对象.处理 .docx 文件
    private HWPFDocument hwpfDocument;//poi word对象.处理 .doc 文件
    private long pageTotal;//总页数
    private long tableTotal;//总表格数
    private long pictureTotal;//总图片数
    private boolean hasTable;//是否有表格
    private boolean hasPicture;//是否有图片
    private Map<Integer, String> textMap;//文本信息<页码，文本内容(整页)>
    private Map<Integer, List<Picture>> pictureMap;//图片信息<页码，图片信息>
    private Map<Integer, List<Table>> tableMap;//表格信息<页码，页码数据（包括文本和表格）>

    public Boolean getIsDoc2Html() {
        if(this.isDoc2Html == null){
            return false;
        }
        return this.isDoc2Html;
    }
    public Boolean getIsDocx2Html() {
        if(this.isDocx2Html == null){
            return false;
        }
        return this.isDocx2Html;
    }

    public void setXwpfDocument(XWPFDocument xwpfDocument) {
        this.xwpfDocument = xwpfDocument;
        this.setAbstractDataDocx(xwpfDocument);
    }

    public void setHwpfDocument(HWPFDocument hwpfDocument,boolean isConvertDocx) {
        this.hwpfDocument = hwpfDocument;
        if(isConvertDocx){
            this.setAbstractDataDocx(docConvertDocx(hwpfDocument));
        }else {
            this.setAbstractDataDoc(hwpfDocument);
        }
    }

    /**
     * @desc TODO 设置文本、表格、图片信息 - docx 文档
     *
     * @param document XWPFDocument
     * @author lujiahong
     * @date 2024/08/12 19:30
     */
    private void setAbstractDataDocx(XWPFDocument document) {
        //页码数量统计
        int pageNum = 1;
        //表格数量统计
        int tableNum = 0;
        //文本信息<页码，文本内容(整页)>
        Map<Integer, StringBuilder> textMap = new HashMap<>();
        //图片信息<页码，图片路径>
        Map<Integer, List<Picture>> pictureMap = new HashMap<>();
        //表格信息<页码，改页码数据（包括文本和表格）>
        Map<Integer, List<Table>> tableMap = new HashMap<>();

        for (IBodyElement element : document.getBodyElements()) {
            // 初始化
            StringBuilder sb_text = textMap.computeIfAbsent(pageNum, k -> new StringBuilder());
            List<Picture> pt = pictureMap.computeIfAbsent(pageNum, k -> new ArrayList<>());
            List<Table> tb = tableMap.computeIfAbsent(pageNum, k -> new ArrayList<>());
            // 区分段落和表格
            if (element instanceof XWPFParagraph) {
                XWPFParagraph paragraph = (XWPFParagraph) element;
                String text = paragraph.getText();
                log.debug("Paragraph text: " + text);
                // <页数，文本内容>
                textMap.put(pageNum, sb_text.append(text).append("\r\n"));
                // <页数，图片信息>
                pictureMap.put(pageNum, this.setPictureMapCommonDocx(pageNum, pt, paragraph.getRuns().stream().flatMap(run -> run.getEmbeddedPictures().stream()).collect(Collectors.toList())));
                // <页码，页码数据（文本）>
                tableMap.put(pageNum,this.setTableMapCommonDocx(pageNum, tb, PLAIN, text));
                // 节属性，判断页码
                if (paragraph.getCTP().getPPr()!=null && paragraph.getCTP().getPPr().getSectPr()!=null) {
                    pageNum ++;
                }
            } else if (element instanceof XWPFTable) {
                tableNum ++;
                XWPFTable table = (XWPFTable) element;
                int rowCount = table.getNumberOfRows();
                for (int i = 0; i < rowCount; i++) {
                    XWPFTableRow row = table.getRow(i);
                    List<XWPFTableCell> cells = row.getTableCells();
                    // <页数，文本内容>
                    textMap.put(pageNum, sb_text.append(cells.stream().map(XWPFTableCell::getText).collect(Collectors.joining("\t"))).append("\r\n"));
                    // <页数，图片信息>
                    pictureMap.put(pageNum, this.setPictureMapCommonDocx(pageNum, pt, cells.stream().flatMap(cell -> cell.getParagraphs().stream()).flatMap(paragraph -> paragraph.getRuns().stream()).flatMap(run -> run.getEmbeddedPictures().stream()).collect(Collectors.toList())));
                    // <页码，页码数据（表格）>
                    for (int j = 0; j < cells.size(); j++) {
                        String tableCellText = cells.get(j).getText();
                        log.debug("Table cell text: " + tableCellText + "\t");
                        tableMap.put(pageNum,this.setTableMapCommonDocx(pageNum, tb, TABLE_WITH_LINE, tableCellText,i,i,j,j));
                    }
                }
            }
        }

        //属性赋值
        this.textMap = textMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toString()));
        this.tableMap = tableMap;
        this.pictureMap = pictureMap;
        this.pageTotal = pageNum;
        this.tableTotal = tableNum;
        this.pictureTotal = pictureMap.values().stream().mapToLong(List::size).sum();
        this.hasTable = tableTotal > 0;
        this.hasPicture = pictureTotal > 0;
        super.setContent(textMap.values().stream().map(StringBuilder::toString).collect(Collectors.joining("\n")));
        super.setOcrPages(tableMap);
    }

    /**
     * @desc TODO 封装表格详细信息
     *
     * @param pageNum 页码
     * @param tbs 表格信息
     * @param ocrType 表格类型
     * @param text 表格文本
     * @param point 单元格坐标 startRow,endRow,startCol,endCol
     * @return java.util.List<com.fi.recogn.dto.table.Table>
     * @author lujiahong
     * @date 2024/08/13 15:11
     */
    private List<Table> setTableMapCommonDocx(int pageNum, List<Table> tbs, ExcelOcrTypeEnum ocrType, String text,Integer... point) {
        if(ocrType == PLAIN && text.trim().isEmpty()){
            return tbs;
        }
        Table table = null;
        List<Line> lines = new ArrayList<>(0);
        List<Cell> tableCells = new LinkedList<>();
        if(CollUtil.isNotEmpty(tbs)){
            int index = tbs.size() - 1;
            Table tb = tbs.get(index);
            List<Line> l = tb.getLines();
            List<Cell> t = tb.getTableCells();
            if(CollUtil.isNotEmpty(l) && CollUtil.isEmpty(t) && ocrType == PLAIN){
                lines = l;
            }else if(CollUtil.isNotEmpty(t) && CollUtil.isEmpty(l) && ocrType == PLAIN){
                table = getTable(pageNum, ocrType, lines, tableCells);
            }
            if(CollUtil.isNotEmpty(t) && CollUtil.isEmpty(l) && ocrType == TABLE_WITH_LINE){
                tableCells = t;
            }else if(CollUtil.isNotEmpty(l) && CollUtil.isEmpty(t) && ocrType == TABLE_WITH_LINE){
                table = getTable(pageNum, ocrType, lines, tableCells);
            }

        }else{
            table = getTable(pageNum, ocrType, lines, tableCells);
        }
        if(ocrType == PLAIN){
            Line line = Line.builder().type(ExcelOcrTypeEnum.TEXT.type()).text(text).build();
            lines.add(line);
        }
        if(ocrType == TABLE_WITH_LINE){
            Cell cell = Cell.builder().isHeader(0).startRow(point[0]).endRow(point[1]).startCol(point[2]).endCol(point[3]).text(text).build();
            tableCells.add(cell);
        }
        if(table != null){
            tbs.add(table);
        }
        return tbs;
    }

    private Table getTable(int pageNum, ExcelOcrTypeEnum ocrType, List<Line> lines, List<Cell> tableCells) {
        return Table.builder()
                .type(ocrType.type())
                .pageNumber(pageNum)
                .lines(lines)
                .tableCells(tableCells)
                .manager(super.getManager())
                .accessId(super.getId())
                .build();
    }

    /**
     * @desc TODO 设置文本、表格、图片信息 - doc 文档
     *
     * @param document HWPFDocument
     * @author lujiahong
     * @date 2024/08/12 19:30
     */
    private void setAbstractDataDoc(HWPFDocument document) {
        //页码数量统计
        int pageNum = 1;
        //表格数量统计
        int tableNum = 0;
        //文本信息<页码，文本内容(整页)>
        Map<Integer, StringBuilder> textMap = new HashMap<>();
        //图片信息<页码，图片路径>
        Map<Integer, List<Picture>> pictureMap = new HashMap<>();
        //表格信息<页码，改页码数据（包括文本和表格）>
        Map<Integer, List<Table>> tableMap = new HashMap<>();

        //属性赋值
        this.textMap = textMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toString()));
        this.tableMap = tableMap;
        this.pictureMap = pictureMap;
        this.pageTotal = pageNum;
        this.tableTotal = tableNum;
        this.pictureTotal = pictureMap.values().stream().mapToLong(List::size).sum();
        this.hasTable = tableTotal > 0;
        this.hasPicture = pictureTotal > 0;
    }

    /**
     * @desc TODO 封装word中图片信息
     * @param pageNum 页码
     * @param pt 图片信息
     * @param pictures 图片信息
     * @author lujiahong
     * @date 2024/08/13 10:00
     */
    private List<Picture> setPictureMapCommonDocx(int pageNum, List<Picture> pt, List<XWPFPicture> pictures) {
        if (!pictures.isEmpty()) {
            pictures.forEach(picture -> {
                Picture pic = Picture.builder()
                        .pageNum(pageNum)
                        .fileName(picture.getPictureData().getFileName())
                        .fileType(getPictureTypeDocx(picture.getPictureData().getPictureType()))
                        .fileSize(picture.getPictureData().getData().length)
                        .fileData(picture.getPictureData().getData())
                        .build();
                pt.add(pic);
            });
        }
        return pt;
    }
    private static String getPictureTypeDocx(int pictureType) {
        switch (pictureType) {
            case Document.PICTURE_TYPE_EMF:
                return "EMF";
            case Document.PICTURE_TYPE_WMF:
                return "WMF";
            case Document.PICTURE_TYPE_PICT:
                return "PICT";
            case Document.PICTURE_TYPE_JPEG:
                return "JPEG";
            case Document.PICTURE_TYPE_PNG:
                return "PNG";
            case Document.PICTURE_TYPE_DIB:
                return "DIB";
            case Document.PICTURE_TYPE_GIF:
                return "GIF";
            case Document.PICTURE_TYPE_TIFF:
                return "TIFF";
            case Document.PICTURE_TYPE_EPS:
                return "EPS";
            case Document.PICTURE_TYPE_BMP:
                return "BMP";
            case Document.PICTURE_TYPE_WPG:
                return "WPG";
            default:
                return "未知类型";
        }
    }

    /**
     * @desc TODO 设置总页数
     * 获取文档中的所有段落，并遍历每个段落中的 CTP 对象。如果 CTP 对象中包含 secp属性，那么表示这是一个分页符，我们就可以通过计数来获取页码信息。
     * @param document XWPFDocument
     * @return int
     * @author lujiahong
     * @date 2024/08/12 18:15
     */
    private static int getPageTotalDocx(XWPFDocument document) {
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        int pageNum = 1;

        for (XWPFParagraph paragraph : paragraphs) {
            CTP ctp = paragraph.getCTP();
            if(ctp.getPPr()!=null && ctp.getPPr().getSectPr()!=null){
                pageNum ++;
            }
        }
        return pageNum;
    }

    /**
     * @desc TODO 设置总表格数
     *
     * @param document XWPFDocument
     * @return int
     * @author lujiahong
     * @date 2024/08/12 18:34
     */
    private static int getTableTotalDocx(XWPFDocument document) {
        int tableNum = 0;
        List<IBodyElement> bodyElements = document.getBodyElements();
        for (IBodyElement element : bodyElements) {
            if (element instanceof XWPFTable) {
                tableNum ++;
            }
        }
        return tableNum;
    }

    /**
     * @desc TODO 设置总图片数
     *
     * @param document XWPFDocument
     * @return int
     * @author lujiahong
     * @date 2024/08/12 18:30
     */
    private static int getPictureTotalDocx(XWPFDocument document) {
        int pictureNum = 0;
        List<IBodyElement> bodyElements = document.getBodyElements();
        for (IBodyElement element : bodyElements) {
            if (element instanceof XWPFParagraph) {
                XWPFParagraph paragraph = (XWPFParagraph) element;
                List<XWPFPicture> pictures = paragraph.getRuns().stream()
                        .flatMap(run -> run.getEmbeddedPictures().stream())
                        .collect(Collectors.toList());
                if (!pictures.isEmpty()) {
                    pictureNum ++;
                }
            } else if (element instanceof XWPFTable) {
                XWPFTable table = (XWPFTable) element;
                int rowCount = table.getNumberOfRows();
                for (int i = 0; i < rowCount; i++) {
                    XWPFTableRow row = table.getRow(i);
                    List<XWPFTableCell> cells = row.getTableCells();
                    for (XWPFTableCell cell : cells) {
                        List<XWPFPicture> pictures = cell.getParagraphs().stream()
                                .flatMap(paragraph -> paragraph.getRuns().stream()
                                        .flatMap(run -> run.getEmbeddedPictures().stream()))
                                .collect(Collectors.toList());
                        if (!pictures.isEmpty()) {
                            pictureNum ++;
                        }
                    }
                }
            }
        }
        return pictureNum;
    }
}